<script setup lang="ts">
  import { TabeConfig, FieldNames } from "@/type"
  import { ref } from 'vue'
  const props = defineProps<TabeConfig>();

  //==========数据定义==============================
  const showSearch = ref<boolean>(true);
  const loading = ref<boolean>(true);

  const isInit = ref<boolean>(false);

  /**搜索框 弹窗开关 */
  const showSerchDialog = ref<boolean>(false);
  /**是否有数据过滤功能 */
  const hasSearch = ref<boolean>(false);
  /**显示搜索功能 */
  const rowHeight = ref<number>(30);
  /**y轴滚动条位置 */
  const positionTop = ref<number>(0);
  /**表格宽度 */
  const tableWidth = ref<number>(1400);
  /**表格显示行数 */
  const showNumber = ref<number>(8);
  /**所有可显示数据的数量 */
  const allDataNumber = ref<number>(1);
  /**表格数据过滤条件 */
  const queryObj = ref<any>({});
  /**当前显示数据 */
  const activeList = ref<Array<any>>([]);
  /**当前显示数据的开始下标 */
  const startNum = ref<number>(0);
  /**当前显示数据搜索结果 */
  const searchData = ref<Array<any>>([]);
  let bigTableHeadDom: HTMLElement;

  let showDataList: Array<any> = [];
  let bomSortList: Array<any> = [];
  let oldDataForSeach: Array<any> = []
  let dataObj: any = {};
  let $options: FieldNames = {
    checkKey: "",
    pid: "",
    id: "",
    rowKey: "",
    isLeafKey: "",
    levelKey: "",
    indeterminateKey: "",
  }
  //==========数据定义=======end=====================

  //==========业务逻辑==============================
  /**数据过滤事件 */
  const getActiveList = () => {
    const start = startNum.value;
    activeList.value = showDataList.slice(
      start,
      start + showNumber.value
    );
  }

  /**数据过滤事件 */
  const searchEvent = (type: number | undefined) => {
    let dataList = bomSortList;
    let pid = $options.pid;
    let id = $options.id;
    let leafKey = $options.isLeafKey;
    let checkKey = $options.checkKey;

    let res = bomSortList;
    let hasKey = false;
    for (let key in queryObj.value) {
      let kv = queryObj.value[key].trim();
      if (kv) {
        hasKey = true;
      }
      res = res.filter(one => one[leafKey] && one[key] == kv)
    }
    if (!hasKey) {
      return;
    }
    if (type === 1) {
      showSerchDialog.value = true;
    }
    const getPNodes = function (item: any, plist: Array<any>) {
      let p = dataList.find(one => one[id] === item[pid])
      if (p && !plist.find(one => one[id] === p[id])) {
        plist.unshift(p)
        getPNodes(p, plist);
      }
    }

    let oldData = res.map(one => {
      let newObj: any = {};
      newObj[id] = one[id];
      newObj[checkKey] = one[checkKey];
      return newObj;
    });

    oldDataForSeach.push(...oldData);
    searchData.value.length = 0;
    for (let i in res) {
      let plist: Array<any> = [];
      getPNodes(res[i], plist);
      searchData.value.push(...plist);
      searchData.value.push(res[i]);
    }
  }

  /**数据行Checkbox点击事件 */
  const checkEvent = (item: any, val: boolean, type: string) => {
    let id = $options.id;
    let checkKey = $options.checkKey;
    let leafKey = $options.isLeafKey;
    let indeterminateKey = $options.indeterminateKey;
    if (type === "callback") {
      item = bomSortList.find((one: any) => one[id] === item[id]);
      if (!item) {
        this.$alert(`节点${item.NodeName}(${item.NodeCode})在Bom里未找到，请联系产品管理员！`, { type: 'error' });
        return;
      }
    }
    item[checkKey] = val;

    let loop = function (node: any, check: boolean) {
      node[checkKey] = check;
      node[indeterminateKey] = false;

      let list = dataObj[node[id]];
      if (list === undefined) return;
      list.forEach((one: any) => {
        one[indeterminateKey] = false;
        loop(one, check);
      });
    };
    //如果不是叶子节点
    if (!item[leafKey]) {
      loop(item, item[checkKey]);
    }

    setPnodeXuanZhong(item);
  }
  /**滚动条位置变更事件 */
  const scrollEvent = (event: any) => {
    const { scrollTop, scrollLeft } = event.target;
    let newScrollTop = Math.ceil(scrollTop);
    let newScrollLeft = Math.ceil(scrollLeft);
    if (Math.abs(bigTableHeadDom?.scrollLeft - newScrollLeft) > 15) {
      bigTableHeadDom.scrollLeft = scrollLeft as number;
      return;
    } else if (Math.abs(positionTop.value - newScrollTop) < 20 && startNum.value > 2) {
      return;
    }
    startNum.value = Math.ceil(newScrollTop / rowHeight.value);
    positionTop.value = newScrollTop;
    getActiveList();
  }


  /**设置上级节点的全选和半选状态 */
  const setPnodeXuanZhong = (item: any) => {
    let dataList = bomSortList;
    let pid = $options.pid;
    let id = $options.id;
    let checkKey = $options.checkKey;
    let indeterminateKey = $options.indeterminateKey;


    let loop = function (node: any) {
      if (!node) return;
      let pnode = dataList.find((one) => one[id] === node[pid]);
      if (pnode) {
        let xiongDiNodes = dataObj[node[pid]];

        let xuanZhong = xiongDiNodes.filter(
          (one: any) => one[checkKey] === true
        ).length;
        //兄弟节点有半选状态，则父级半选
        if (xuanZhong === 0) {
          pnode[indeterminateKey] = xiongDiNodes.find((one: any) => one[indeterminateKey]) !== undefined;
        }
        else {
          pnode[indeterminateKey] = xuanZhong !== xiongDiNodes.length;
        }
        pnode[checkKey] = xuanZhong === xiongDiNodes.length;
      }
      loop(pnode);
    };
    loop(item);
  }

  /**树节点单元格点击事件
   * 可实现下级展开和收起
   */
  const colClickEvent = (item: any) => {

    if ($options.isLeafKey && item[$options.isLeafKey]) {
      // item.IsSelect = true;
    } else {
      toggleExpand(item);
    }
  }
  /**数据搜索弹窗确认事件
   * 保存数据变更
   */
  const serchDialogSave = () => {
    oldDataForSeach.length = 0;
    showSerchDialog.value = false;
    $emit('searchClose');
  }
  /**数据搜索弹窗取消事件
     * 取消操作，还原数据变更
     */
  const serchDialogCancel = () => {
    let id = $options.id;
    let checkKey = $options.checkKey;
    let dataList = bomSortList;

    oldDataForSeach.forEach(one => {
      let t = searchData.value.find(o => o[id] === one[id]);
      if (!t) {
        t = dataList.find(o => o[id] === one[id]);
      } if (!t) {
        alert('组件出错！')
        return;
      }
      t[checkKey] = one[checkKey];
    });
    oldDataForSeach.length = 0;
    showSerchDialog.value = false;
  }
  /**展开或收启下级时 变更节点状态是否展开 */
  const setTreeStatus = (children: Array<any>, status: boolean = false) => {
    let b = new Date();
    let id = $options.id;
    const travel = (list: Array<any>) => {
      list.forEach((child) => {
        child.isShow = status;
        let nextChildren = dataObj[child[id]];
        if (nextChildren) {
          // 展开则只展开子集中的isExpand为true的
          // 收拢则全部收拢
          if ((status && child.isExpand) || !status) travel(nextChildren);
        }
      });
    };
    travel(children);
    console.info("=====setTreeStatus=========", new Date() - b);
    getShowData();
    loading.value = false;
  }

  /**展开下级事件 */
  const toggleExpand = (item: any) => {
    let a = new Date();
    let isExpand = item.isExpand;
    item.isExpand = !isExpand;

    let pid = $options.pid;
    let id = $options.id;
    let children = bomSortList.filter(
      (one: any) => one[pid] === item[id]
    );
    if (children.length) {
      setTreeStatus(children, !isExpand);
    }
    console.info("=====toggleExpand=========", new Date() - a);
  }


  /** 获取可以被显示的数据，即不被收缩的数据
    * 同时对数据显示顺序排序，受父节点位置影响，即子级紧跟父级
  */
  const getShowData = () => {
    showDataList = bomSortList.filter(
      (item: any) => item.isShow
    );
    allDataNumber.value = showDataList.length;
    if (allDataNumber.value < showNumber.value) {
      allDataNumber.value = showNumber.value;
    }
    getActiveList();
  }



  //==========业务逻辑=======end=======================
  /**获取选中节点，包含全选和半选 */
  const getCheckData = () => {
    let checkKey = $options.checkKey;
    let indeterminateKey = $options.indeterminateKey;
    return bomSortList.filter(
      (item: any) => item[checkKey] || item[indeterminateKey]
    );
  }



</script>

<style scope>
  .bigTable dev {
    border: 1px solid rgb(0, 0, 0);
    color: #909399;
    font-weight: 500;
  }

  .bigTable {
    scrollbar-width: 10px;
    scrollbar-base-color: green;
    scrollbar-track-color: red;
    scrollbar-arrow-color: blue;
  }

  .bigTable .table-head {
    color: #909399;
    font-weight: 700;
    overflow-x: hidden;
    width: calc(100% - 10px);
  }

  .scroll-box {
    position: relative;
    overflow-x: auto;
    overflow-y: auto;
    /* border: 1px solid rgb(0, 0, 0); */
  }

  .scroll-box-head {
    background-color: #f8f8f9;
  }

  .scroll-box-head .is-line {
    position: absolute;
    right: -7px;
    bottom: 0;
    width: 14px;
    height: 100%;
    text-align: center;
    z-index: 1;
    cursor: col-resize;
  }

  .scroll-head {
    padding: 12px 0;
    display: inline-block;
    vertical-align: middle;
    color: #606266;
  }

  .scroll-head span {
    white-space: pre-line;
    word-break: break-all;
    font-variant: tabular-nums;
    font-feature-settings: "tnum";
  }

  .scroll-data {
    overflow: hidden;
    position: absolute;
    width: 100%;
  }

  .scroll-item {
    border: 1px solid #f8f8f9;
    height: 30px;
    width: 100%;
    overflow: hidden;
  }

  .scroll-item .scroll-col {
    /* height:30px; */
    display: inline-block;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 6px 0;
    white-space: nowrap;
  }

  .scroll-col-check {
    text-align: center;
  }

  .scroll-item:hover {
    width: 100%;
    background: rgb(104, 111, 211);
    color: #fff;
  }

  .level_1 {
    font-weight: 700;
  }

  .bigTable ::-webkit-scrollbar {
    width: 8px;
    height: 5px;
    opacity: 0.2;
  }

  .bigTable ::-webkit-scrollbar-thumb {
    background: #ccc;
    /**滑块颜色*/
    border-radius: 5px;
    /**滑块圆角*/
  }
</style>

<template>
  <div style="width: 100%" class="bigTable">
    <div class="table-head" id="bigTableHead">
      <div class="scroll-box-head" :style="`width:${tableWidth}px;`">
        <slot name="check">
          <div style="width: 50px" class="scroll-head scroll-col-check">
            <span>选择</span>
          </div>
        </slot>
        <slot name=”head“>
          <div v-for="col in $options.columns" :key="col.name" :style="`width: ${col.width}px`" class="scroll-head">

            <span v-show="!showSearch">{{ col.title }}</span>
            <template v-if="col.isSearch">
              <span v-show="showSearch"><el-input @clear="showSearch = false" clearable
                 :placeholder="`输入${col.searchPlaceholder || col.title}精确搜索`" v-model="queryObj[col.name]" size="mini"
                 style="width:80%"><el-button slot="append" icon="el-icon-search"
                   @click.stop="searchEvent(1)"></el-button></el-input></span>
              <i v-show="!showSearch" @click.stop="showSearch = true" class="el-icon-search" title="搜索"
               style="font-size: 16px;margin-left: 5px; cursor: pointer;color: #68b92e;" size="mini"></i>
            </template>
          </div>
        </slot>
      </div>
    </div>
    <div ref="demo" class="scroll-box demo" :style="`height: ${showNumber * rowHeight + 10}px;`"
     @scroll.stop="scrollEvent">
      <div class="scroll-blank" :style="`width:${tableWidth - 20}px;height: ${allDataNumber * rowHeight
        }px;min-height:300px;`">
        <span v-show="activeList.length === 0">暂无数据</span>
      </div>
      <div class="scroll-data" :style="`top: ${positionTop}px;width:${tableWidth - 20}px;`" v-loading="loading">
        <div v-for="item in activeList" :key="item[$options.rowKey]" class="scroll-item">
          <div style="width: 50px" class="scroll-col scroll-col-check">
            <el-checkbox :indeterminate="item[$options.indeterminateKey]" v-model="item[$options.checkKey]"
             @change="(val) => checkEvent(item, val)"></el-checkbox>
          </div>
          <template v-for="(col, index) in $options.columns">
            <div v-if="col.isTreeNode" :key="col.name" :style="`width:${col.width}px; cursor: pointer`"
             class="scroll-col" @click.stop="colClickEvent(item)">
              <span :style="`padding-left: ${item[$options.isLeafKey]
                ? item.Level * 15 - 5
                : item.Level * 15 - 15
                }px;`" :class="{ level_1: item.Level === 1 }">
                <i class="arrow" :class="item.isExpand
                  ? 'el-icon-caret-bottom'
                  : 'el-icon-caret-right'
                  " v-if="!item[$options.isLeafKey]" @click.stop="toggleExpand(item)"></i>
              </span>
              <span v-if="col.formatterHtml" v-html="col.formatterHtml(item)"></span>
              <span v-else-if="col.formatter">{{ col.formatter(item) }}</span>
              <span v-else> {{ item[col.name] }}</span>
            </div>
            <div v-else :key="index" :style="`width:${col.width}px;`" class="scroll-col" :title="item[col.name]">
              <span v-if="col.formatterHtml" v-html="col.formatterHtml(item)"></span>
              <span v-else-if="col.formatter">{{ col.formatter(item) }}</span>
              <span v-else> {{ item[col.name] }}</span>
            </div>
          </template>
        </div>
      </div>
    </div>
    <!-- 部件查询部分 -->
    <template v-if="hasSearch">
      <el-dialog title="搜索结果" :visible.sync="showSerchDialog" :show-close='false' :close-on-click-modal='false'
       append-to-body>
        <div style="overflow: auto;width:100%">
          <div class="table-head search" :style="`width:${tableWidth}px;`">
            <div class="scroll-box-head">
              <slot name="check">
                <div style="width: 50px" class="scroll-head scroll-col-check">
                  <span>选择</span>
                </div>
              </slot>
              <slot name=”head“>
                <div v-for="col in $options.columns" :key="col.name" :style="`width: ${col.width}px`"
                 class="scroll-head search">
                  <span v-if="col.isSearch"><el-input clearable
                     :placeholder="`输入${col.searchPlaceholder || col.title}精确搜索`" v-model="queryObj[col.name]"
                     size="mini" style="width:80%"><el-button slot="append" icon="el-icon-search"
                       @click.stop="searchEvent(2)"></el-button></el-input></span>
                  <span v-else>{{ col.title }}</span>
                </div>
              </slot>
            </div>
          </div>

          <div class="scroll-data-search"
           :style="`width:${tableWidth}px;height: ${searchData.length * rowHeight}px;min-height:200px;`">
            <div v-for="item in searchData" :key="item[$options.rowKey]" class="scroll-item">
              <div style="width: 50px" class="scroll-col scroll-col-check">
                <el-checkbox :indeterminate="item[$options.indeterminateKey]" v-model="item[$options.checkKey]"
                 @change="(val) => checkEvent(item, val)"></el-checkbox>
              </div>
              <template v-for="(col, index) in $options.columns">
                <div v-if="col.isTreeNode" :key="col.name" :style="`width:${col.width}px;`" class="scroll-col">
                  <span :style="`padding-left: ${item[$options.isLeafKey]
                    ? item.Level * 15 - 5
                    : item.Level * 15 - 15
                    }px;`" :class="{ level_1: item.Level === 1 }">
                    <i class="arrow el-icon-caret-bottom" v-if="!item[$options.isLeafKey]"></i>
                    {{ item.NodeName }}({{ item.NodeCode }})
                  </span>
                </div>
                <div v-else :key="index" :style="`width:${col.width}px;`" class="scroll-col" :title="item[col.name]">
                  {{ item[col.name] }}
                </div>
              </template>
            </div>
          </div>
        </div>
        <span slot="footer" class="dialog-footer">
          <el-button type="primary" size="small" @click="serchDialogSave">确 定</el-button>
          <el-button size="small" @click="serchDialogCancel">取 消</el-button>
        </span>
      </el-dialog>
    </template>

  </div>
</template>
